package com.example.myapplicationndk.opengl.camerax;

import android.content.Context;
import android.graphics.SurfaceTexture;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.util.Log;
import android.view.Surface;

import androidx.annotation.NonNull;
import androidx.camera.core.Preview;
import androidx.camera.core.SurfaceRequest;
import androidx.core.util.Consumer;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class CameraRender implements Preview.SurfaceProvider, GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener {

    private Callback mCallback;
    private ExecutorService mExecutor;
    private Context mContext;
    private String TAG = "CameraRender";
    private int[] textures = new int[1];
    private SurfaceTexture mSurfaceTexture;
    private ScreenFilter screenFilter;
    /*
     if (mtx.length != 16) {
            throw new IllegalArgumentException();
        }
     */
    private float[] mtx = new float[16];

    public CameraRender(Context context, Callback callback) {
        mContext = context;
        mCallback = callback;
        mExecutor = Executors.newSingleThreadExecutor();
    }


    /**
     * CameraX使用Preview.SurfaceProvider获取摄像头数据到surface，
     * 因此只需要把surface与自定义的SurfaceTexture关联起来即可。
     * 生成一个texture，然后用其创建一个SurfaceTexture，
     * 后文使用这个SurfaceTexture创建Surface并提供给Camera使用。
     * 因此，Camera得到的图像数据->Surface->SurfaceTexture->Texture。
     */
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // 设置调用glClear(...)清空屏幕时的颜色数据，glClear(...)的作用时set clear color
        GLES20.glClearColor(0f, 0f, 0f, 1f);

        GLES20.glGenTextures(textures.length, textures, 0);
        Log.d(TAG, "texture: " + textures[0]);
        // 构造新的 SurfaceTexture 以将图像流式传输到给定的 OpenGL 纹理，即传给textures[0]
        mSurfaceTexture = new SurfaceTexture(textures[0]);
        screenFilter = new ScreenFilter(mContext);
    }


    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
        if (mCallback != null) {
            mCallback.onSurfaceChanged();
        }
    }

    @Override
    public void onSurfaceRequested(@NonNull SurfaceRequest request) {
        Log.d(TAG, "onSurfaceRequested, request: " + request);
        if (mSurfaceTexture != null) {
            mSurfaceTexture.setOnFrameAvailableListener(this);
            mSurfaceTexture.setDefaultBufferSize(request.getResolution().getWidth(), request.getResolution().getHeight());
            /// 绘制到Surface上的图像帧，会提供给SurfaceTexture。
            /// 当调用SurfaceTexture.updateTexImage时，此图像帧会被附加到OpenGL ES Texture上。
            Surface surface = new Surface(mSurfaceTexture);
            Consumer<SurfaceRequest.Result> resultListener = new Consumer<SurfaceRequest.Result>() {
                @Override
                public void accept(SurfaceRequest.Result result) {
                    Log.d(TAG, "resultListener be called!");
                    surface.release(); // TODO: 为什么需要？
                    mSurfaceTexture.release(); // TODO: 为什么需要？
                }
            };
            // 通过 request.provideSurface 函数，把 surface 提供给 camera。
            request.provideSurface(surface, mExecutor, resultListener);
        }
    }


    @Override
    public void onDrawFrame(GL10 gl) {
        if (mSurfaceTexture == null) {
            return;
        }
        /// glClear(...)填充屏幕使用的颜色，是最后一次调用glClearColor(...)的颜色
        GLES20.glClearColor(1f, 0f, 0f, 1f);
        /// 清空屏幕，擦书屏幕上的所有颜色，并用glClearColor(...)设置的颜色充满整个屏幕
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

        // 更新到最新帧，并且此图像帧会被附加到OpenGL ES Texture上（new SurfaceTexture(textures[0])构造时传入的）。
        mSurfaceTexture.updateTexImage();

        mSurfaceTexture.getTransformMatrix(mtx);
        screenFilter.setTransformMatrix(mtx);
        // 通知GPU处理帧数据
        screenFilter.onDrawFrame(textures[0]);
    }

    @Override
    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
        if (mCallback != null) {
            mCallback.onFrameAvailable();
        }
    }

    public interface Callback {

        public void onSurfaceChanged();

        public void onFrameAvailable();
    }
}
